//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//********************************************************* 

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace Microsoft.TridentWordAddIn.Common
{
    public static class ImageExtensions
    {
        /// <summary>
        /// Serializeds the image.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>byte[]</returns>
        public static byte[] SerializedImage(this Image image)
        {
            if (image == null)
            {
                return null;
            }

            using (MemoryStream memoryStream = new MemoryStream())
            {
                BinaryFormatter binaryFormatter = new BinaryFormatter();
                binaryFormatter.Serialize(memoryStream, image);

                byte[] serializedImage = memoryStream.ToArray();
                return serializedImage;
            }
        }

        /// <summary>
        /// Deserializeds the image.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>Image</returns>
        public static Image DeserializedImage(byte[] value)
        {
            if (value != null)
            {
                using (MemoryStream memoryStream = new MemoryStream(value))
                {
                    BinaryFormatter binaryFormatter = new BinaryFormatter();
                    return (Image)binaryFormatter.Deserialize(memoryStream);
                }
            }

            return null;
        }

        /// <summary>
        /// Gets the thumbnail image.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Image</returns>
        public static Image GetThumbnailImage(this Image image)
        {
            if (image == null)
            {
                return null;
            }

            Image.GetThumbnailImageAbort dummyCallBack =
                new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback);

            int thumbSize = 64;
            Image thumbnail = image.GetProportionedThumbnail(thumbSize, dummyCallBack);

            return thumbnail;
        }

        /// <summary>
        /// Formats the link image.
        /// </summary>
        /// <param name="linkImage">The link image.</param>
        /// <param name="text">The text.</param>
        /// <returns>Image</returns>
        public static Image FormatLinkImage(this Image linkImage, string text)
        {
            int padding = 2;
            int extraWidth = 100;
            Font font = new Font("arial", 12, FontStyle.Bold, GraphicsUnit.Pixel);
            Image finalImage = new Bitmap(linkImage.Width + extraWidth + 2 * padding, linkImage.Height);
            Graphics graphics = Graphics.FromImage(finalImage);
            float width = GetTextWidth(font, graphics, text);

            if (width > extraWidth)
            {
                text += "...";
                do
                {
                    width = GetTextWidth(font, graphics, text);
                    if (width > extraWidth)
                    {
                        text = text.Substring(0, text.Length - 4);
                        text += "...";
                    }
                } while (width > extraWidth);
            }

            // shorten image for small text strings
            //extraWidth = (int)width;
            // change because text is ignored:
            extraWidth = 0;
            padding = 0;

            finalImage = new Bitmap(linkImage.Width, linkImage.Height);
            graphics = Graphics.FromImage(finalImage);
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            Pen solidWhite = new Pen(new SolidBrush(Color.White), 20);

            Rectangle imageBounds = new Rectangle(new Point(0, 0), finalImage.Size);
            imageBounds.Width -= 1;
            imageBounds.Height -= 1;
            graphics.DrawRectangle(solidWhite, imageBounds);
            graphics.DrawImage(linkImage, 0, 0);

            RectangleF rectF = new RectangleF(linkImage.Width, padding, finalImage.Width - linkImage.Width, finalImage.Height);
            rectF.Location = new PointF(rectF.Location.X - 1, rectF.Location.Y - 1);

            font.Dispose();
            solidWhite.Dispose();

            return finalImage;
        }
        
        /// <summary>
        /// Thumbnails the callback.
        /// </summary>
        /// <returns>bool</returns>
        public static bool ThumbnailCallback()
        {
            return false;
        }

        /// <summary>
        /// Gets the proportioned thumbnail.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="maxPixels">The max pixels.</param>
        /// <param name="thumbnailCallback">The thumbnail callback.</param>
        /// <returns>Image</returns>
        private static Image GetProportionedThumbnail(this Image image, int maxPixels, Image.GetThumbnailImageAbort thumbnailCallback)
        {
            float scaleFactor = 1;
            if (image.Width > image.Height)
            {
                if (image.Width > maxPixels)
                {
                    scaleFactor = (float)maxPixels / (float)image.Width;
                }
            }
            else
            {
                if (image.Height > maxPixels)
                {
                    scaleFactor = (float)maxPixels / (float)image.Height;
                }
            }

            int newH = (int)(scaleFactor * image.Height);
            int newW = (int)(scaleFactor * image.Width);
            if (image.Width > image.Height && image.Width > maxPixels)
            {
                newW = maxPixels; // can't trust floating point stuff, something newH is maxPixels-1
            }
            else if (image.Height > maxPixels)
            {
                newH = maxPixels;
            }

            Image thumbnailImg = image.GetThumbnailImage(newW, newH, thumbnailCallback, IntPtr.Zero);
            Bitmap thumbnail = new Bitmap(thumbnailImg);
            int thumbSize = maxPixels;
            // if it's not square, we need to add some whitespace padding since
            // the display will stretch it into a square

            Bitmap paddedThumb = new Bitmap(thumbSize, thumbSize);
            using (Graphics gr = Graphics.FromImage(paddedThumb))
            {
                gr.FillRectangle(Brushes.White, new Rectangle(0, 0, maxPixels, maxPixels));
                int x = (int)(((float)maxPixels / 2) - ((float)thumbnail.Width / 2));
                int y = (int)(((float)maxPixels / 2) - ((float)thumbnail.Height / 2));
                gr.DrawImage(thumbnail, x, y, thumbnail.Width, thumbnail.Height);
            }

            return paddedThumb;
        }

        /// <summary>
        /// Gets the width of the text.
        /// </summary>
        /// <param name="font">The font.</param>
        /// <param name="graphics">The graphics.</param>
        /// <param name="text">The text.</param>
        /// <returns>float</returns>
        private static float GetTextWidth(Font font, Graphics graphics, string text)
        {
            // measure predicted width of text.  note that the rectangle is overly wide
            SizeF size = graphics.MeasureString(text, font, new SizeF(1000, 1000));
            return size.Width;
        }
    }
}
